package com.example.myblog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.example.myblog.dto.BlogMapDTO;
import com.example.myblog.dto.BlogMessageDTO;
import com.example.myblog.dto.BlogSearchDTO;
import com.example.myblog.entity.*;
import com.example.myblog.exception.BaseException;
import com.example.myblog.mapper.BlogMapper;
import com.example.myblog.result.BaseCodeEnum;
import com.example.myblog.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.myblog.utils.ElasticUtil;
import com.example.myblog.component.EntityVoToEntityUtil;
import com.example.myblog.utils.UserThreadLocal;
import com.example.myblog.vo.BlogVO;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 博客 服务实现类
 * </p>
 *
 * @author 叶兴
 * @since 2022-09-29
 */
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements BlogService {

    BlogMapper blogMapper;

    BlogMessageService blogMessageService;

    EntityVoToEntityUtil entityVoToEntityUtil;
    CategoryService categoryService;
    BlogTagService blogTagService;

    ElasticUtil elasticUtil;
    @Autowired
    public void setElasticUtil(ElasticUtil elasticUtil) {
        this.elasticUtil = elasticUtil;
    }

    @Autowired
    public void setBlogTagService(BlogTagService blogTagService) {
        this.blogTagService = blogTagService;
    }

    @Autowired
    public void setCategoryService(CategoryService categoryService) {
        this.categoryService = categoryService;
    }

    @Autowired
    public void setEntityVoToEntityUtil(EntityVoToEntityUtil entityVoToEntityUtil) {
        this.entityVoToEntityUtil = entityVoToEntityUtil;
    }

    TagService tagService;
    @Autowired
    public void setTagService(TagService tagService) {
        this.tagService = tagService;
    }

    @Autowired
    public void setBlogMessageService(BlogMessageService blogMessageService) {
        this.blogMessageService = blogMessageService;
    }


    @Autowired
    public void setBlogMapper(BlogMapper blogMapper) {
        this.blogMapper = blogMapper;
    }




    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertBlog( BlogVO blogVO) {
        // 1. 将前台传回的数据转换成易于识别的数据
        BlogMessageDTO blogMessageDTO = entityVoToEntityUtil.changeFromBlogVO(blogVO);
        BlogMessage blogMessage = new BlogMessage();
        Blog blog = new Blog();
        BeanUtils.copyProperties(blogMessageDTO,blogMessage);
        BeanUtils.copyProperties(blogMessageDTO,blog);
        // 2. 补充数据赋值
        String username = UserThreadLocal.getUserDtoName();
        String id = username + LocalDateTime.now();
        blogMessage.setId(id);
        blogMessage.setUserName(username);
        blogMessage.setBlogCreated(LocalDateTime.now());
        Category category = categoryService.getById(blogVO.getCate());
        blogMessage.setBlogCategory(category.getName());
        blogVO.getTagList().forEach(tagId -> {
            BlogTag blogTag = new BlogTag();
            blogTag.setBlogId(id);
            blogTag.setTagId(tagId);
            blogTagService.save(blogTag);
        });
        // 1. 将文章信息进行插入
        blogMessageService.insertBlogMessage(blogMessage);
        // 2. 将 id 传给 blog 并插入
        blog.setBlogId(id);
        blogMapper.insert(blog);
        return 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteBlog(String id) throws BaseException {
        UpdateWrapper<BlogMessage> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id",id).set("blog_state",0);
        boolean update = blogMessageService.update(updateWrapper);
        if (!update) {
            throw new BaseException(BaseCodeEnum.DELETE_FAILED);
        }
        return 1;
    }

    @Override
    public BlogMessageDTO getOneBlog(String id) {
        QueryWrapper<Blog> wrapper = new QueryWrapper<>();
        wrapper.eq("blog_id",id);
        Blog blog = getOne(wrapper);
        QueryWrapper<BlogMessage> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id",id);
        BlogMessage message = blogMessageService.getOne(queryWrapper);
        List<Tag> tags = tagService.getTagsByBlogId(id);
        BlogMessageDTO dto = new BlogMessageDTO();
        BeanUtils.copyProperties(blog,dto);
        BeanUtils.copyProperties(message,dto);
        dto.setTags(tags);
        return dto;
    }

    @Override
    public Map<String, List<BlogMapDTO>> getBlogArchives() {
        String username = UserThreadLocal.getUserDtoName();
        QueryWrapper<BlogMessage> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_name",username).eq("blog_state",1);
        List<BlogMessage> list = blogMessageService.getBaseMapper().selectList(queryWrapper);
        List<BlogMapDTO> mapDTOS = list.stream().map(blogMessage -> {
            BlogMapDTO mapDTO = new BlogMapDTO();
            mapDTO.setTitle(blogMessage.getBlogTitle());
            mapDTO.setId(blogMessage.getId());
            mapDTO.setDate(blogMessage.getBlogCreated().toLocalDate());
            return mapDTO;
        }).collect(Collectors.toList());
        Map map = listToMap(mapDTOS, mapDTOS.size());
        return map;
    }

    @Override
    public int blogCount() {
        String username = UserThreadLocal.getUserDtoName();
        QueryWrapper<BlogMessage> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_name",username).eq("blog_state",1);
        Integer count = blogMessageService.getBaseMapper().selectCount(queryWrapper);
        if (count == null) {
            return 0;
        } else {
            return count;
        }

    }

    @Override
    public List<BlogSearchDTO> searchBlog(String query) {
        // 1. 构建查询条件
        MatchQueryBuilder builder = QueryBuilders.matchQuery("all", query);
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(builder)
                .withPageable(PageRequest.of(0,2))
                .build();
        List<Blog> blogList = elasticUtil.searchAll(searchQuery, Blog.class);
        List<BlogMessage> blogMessages = elasticUtil.searchAll(searchQuery, BlogMessage.class);
        // 2. 封装 es 的查询结果
        List<BlogSearchDTO> res = new ArrayList<>(4);
        // 2.1 先查询博客信息
        blogMessages.forEach(blogMessage -> {
            BlogSearchDTO dto = new BlogSearchDTO();
            BeanUtils.copyProperties(blogMessage,dto);
            dto.setBlogContent(blogMessage.getBlogDescription());
            res.add(dto);
        });
        // 2.2 再查询博客详情,如果之前的查询已经有相同博客的数据返回了，那就跳过
        blogList.forEach(blog -> {
            List<String> list = res.stream().map(BlogSearchDTO::getId).collect(Collectors.toList());
            if (!list.contains(blog.getBlogId())) {
                BlogSearchDTO dto = new BlogSearchDTO();
                BlogMessage message = blogMessageService.getById(blog.getBlogId());
                dto.setBlogTitle(message.getBlogTitle());
                dto.setUserName(message.getUserName());
                dto.setId(message.getId());
                dto.setBlogContent(blog.getBlogText());
                res.add(dto);
            }

        });
        // 3. 如果es里没有查到数据，就再去数据库中查询
        if (res.size() <= 0) {
            QueryWrapper<Blog> wrapper = new QueryWrapper<>();
            wrapper.like("blog_text",query);
            List<Blog> list1 = blogMapper.selectList(wrapper);
            QueryWrapper<BlogMessage> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("blog_title",query).or().like("blog_description",query);
            List<BlogMessage> messages = blogMessageService.getBaseMapper().selectList(queryWrapper);
            messages.forEach(blogMessage -> {
                BlogSearchDTO dto = new BlogSearchDTO();
                BeanUtils.copyProperties(blogMessage,dto);
                dto.setBlogContent(blogMessage.getBlogDescription());
                res.add(dto);
            });
            list1.forEach(blog -> {
                List<String> list = res.stream().map(BlogSearchDTO::getId).collect(Collectors.toList());
                if (!list.contains(blog.getBlogId())) {
                    BlogSearchDTO dto = new BlogSearchDTO();
                    BeanUtils.copyProperties(blog,dto);
                    dto.setBlogContent(blog.getBlogText());
                    res.add(dto);
                }

            });

        }
        return res;
    }

    private Map listToMap(List<BlogMapDTO> list,int size) {
        Map<String, List<BlogMapDTO>> map = new HashMap<>(size);
        for (int i = 0; i < list.size(); i++) {
            String key =
                    list.get(i).getDate().getYear() +"年" + list.get(i).getDate().getMonthValue() + "月";
            if (map.containsKey(key)) {
                map.get(key).add(list.get(i));
            } else {
                List<BlogMapDTO> blogMapDTOS = new ArrayList<>();
                blogMapDTOS.add(list.get(i));
                map.put(key,blogMapDTOS);
            }
        }
        return map;
    }


}
